/**
 * 一、与 JavaScript 一样，TypeScript 中也使用 extends 关键字来实现继承：
 * 
 * 对于这里的两个类，比较严谨的称呼是 基类（Base） 与 派生类（Derived）。
 * 当然，如果叫父类与子类更容易理解也没问题。
 * 关于基类与派生类，需要了解的主要是 派生类 对 基类成员的访问 与 覆盖操作。
*/

class Base { }

class Derived extends Base { }

/**
 * 派生类中可以访问到使用 public 或 protected 修饰符的基类成员。
 * 除了访问以外，基类中的方法也可以在派生类中被覆盖，但仍然可以通过 super 访问到基类中的方法
*/
class BaseA {
    print() { }
}

class DerivedA extends BaseA {
    print() {
        super.print()
        // ...
    }
}

/**
 * 二、TypeScript 4.3 新增了 override 关键字，来确保 派生类 尝试 覆盖的方法 一定在基类中存在定义
 * 这一关键字能 确保首先这个方法在基类中存在，同时标识这个方法在 派生类 中被覆盖了
*/
class BaseB {
    printWithLove() {

    }
}
//应为“;”报错，不影响使用语法说明，不知道为什么报这个错
//正确提示应为：尝试覆盖的方法并未在基类中声明
class DerivedB extends BaseB {
    override printWithLove() {
        // ...
    }
}

/**
 * 三、抽象类：使用 abstract 关键字声明
 * 一个抽象类描述了一个类中应当有哪些成员（属性、方法等），
 * 一个抽象方法描述了这一方法在实际实现中的结构。
 * 类的方法和函数非常相似，包括结构，
 * 因此抽象方法其实描述的就是这个 方法的入参类型 与 返回值类型。
 * 
 * 抽象类中的成员也需要使用 abstract 关键字才能被视为抽象类成员，如这里的抽象方法。
*/
abstract class AbsFoo {
    abstract absProp: string;
    abstract get absGetter(): string;
    abstract absMethod(name: string): string
}

//实现（implements）一个抽象类，必须完全实现这个抽象类的 每一个抽象成员。
//需要注意的是，在 TypeScript 中无法声明 静态的抽象成员
class Foo implements AbsFoo {
    absProp: string = "hanxuming"

    get absGetter() {
        return "hanxuming"
    }

    absMethod(name: string) {
        return name
    }
}

/**
 * 四、抽象类 与 接口interface：
 * 对于抽象类，它的本质就是描述类的结构。
 * interface 不仅可以声明函数结构，也可以声明类的结构：
 * */
interface FooStructA {
    absProp: string;
    absGetter(): string;
    absMethod(input: string): string
}

class FooA implements FooStructA {
    absProp: string = "hanxuming"

    absGetter() {
        return "hanxuming"
    }

    absMethod(name: string) {
        return name
    }
}
//五、还可以使用 Newable Interface 来描述一个类的结构（类似于描述函数结构的 Callable Interface）
//描述函数结构的 Callable Interface，在 07_函数_1、函数类型签名.ts 中有介绍。
class FooB { }

interface FooStructB {
  new(): FooB
}

declare const NewableFoo: FooStructB;

const fooB = new NewableFoo();

//export {}：解决“无法重新声明块范围变量”错误提示问题
export { }